<?php

namespace App\Http\Services\Api\V1\App\Product;

use App\Http\Requests\Order\OrderIndexRequest;
use App\Http\Services\Api\V1\App\Cart\ShoppingCartService;
use App\Jobs\AutoCancelOrder;
use App\Models\Currency;
use App\Models\Customers\Customer;
use App\Models\Order\Address;
use App\Models\Order\Extension;
use App\Models\Order\Order;
use App\Models\Order\OrderFlowStatus;
use App\Models\Order\OrderHistory;
use App\Models\Order\OrderOption;
use App\Models\Order\OrderProduct;
use App\Models\Order\OrderStatus;
use App\Models\Order\OrderTotal;
use App\Models\Products\Product;
use App\Models\Products\ProductOption;
use App\Models\Products\ProductOptionValue;
use App\Models\ReturnProduct\ReturnProduct;
use App\Models\Setting;
use App\Models\Shipping\GeoZone;
use App\Models\Shipping\ZoneToGeoZone;
use App\Services\Kdniao\ExpressCodeMap;
use App\Services\Kdniao\Kdniao;
use App\Services\Locale\Language;
use App\Services\MobileApp\AppEnv;
use App\Services\Utils\ApiChecker;
use App\Services\Utils\FormatPrice;
use App\Services\Utils\Guard;
use App\Services\Utils\ImageHelper;
use App\Services\Utils\TimeHelper;
use App\Services\Utils\Transaction;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;

class OrderService
{
    use ApiChecker;

    private static $geo_zone_id;

    /**
     * @param OrderIndexRequest $request
     * @return LengthAwarePaginator
     * @throws \Exception
     */
    public function getOrderData(OrderIndexRequest $request)
    {
        /** @var Customer $customer */
        $customer = Guard::customer();

        // 未支付过期订单自动删除（有效期：1天）,可以放消息队列中
        $this->deleteOrderIsExpired($customer);

        $order_flow_status_id = $request->order_flow_status_id;
        $order_status_ids = OrderFlowStatus::getOrderStatusIds($order_flow_status_id);
        /** @var LengthAwarePaginator $orders */
        $orders = (new Order)->filterOrderStatusIds($order_status_ids)
//            ->filterCancelOrder()
            ->filterIsToBeEvaluate($order_flow_status_id)
            ->where('customer_id', $customer->customer_id)
            ->with('orderProducts.product', 'orderTotalByTotal', 'orderStatusByLanguage')
            ->latest('date_added')
            ->paginate($request->per_page ?: 10);

        $this->transformOrders($orders, $order_status_ids);

        return $orders;
    }

    /**
     * @return OrderFlowStatus[]|Collection
     */
    public function getOrderStatusList()
    {
        $order_flow_status = (new OrderFlowStatus)->where('status', OrderFlowStatus::ORDER_FLOW_STATUS_ON)
            ->where('language_id', AppEnv::languageId())
            ->oldest('sort_order')
            ->get();

        return $order_flow_status;
    }

    /**
     * @param $order_id
     * @return array
     * @throws \Exception
     */
    public function getOrderDetail($order_id)
    {
        /** @var Customer $customer */
        $customer = Guard::customer();

        /** @var Order $order */
        $order = (new Order)->where('order_id', $order_id)
            ->where('customer_id', $customer->customer_id)
            ->with(
                'orderProducts.product',
                'orderProducts.orderOptions',
                'orderTotals',
                'orderStatusByLanguage',
                'orderHistoryByPayment',
                'orderAutoSign'
            )
            ->first();

        $this->checkItemExist($order);

        /** @var Currency $currency */
        $currency = Currency::getCurrencyByCode($order->currency_code);

        $order_detail = $this->assembleOrderDetail($order, $currency);

        return $order_detail;
    }

    /**
     * @param LengthAwarePaginator $orders
     * @param array $order_status_ids
     */
    private function transformOrders(LengthAwarePaginator $orders, array $order_status_ids)
    {
        $order_data = $orders->getCollection();

        $order_data->transform(function(Order $order) use($order_status_ids){
            $product_count = $order->orderProducts->sum('quantity');
            $has_review = Order::isHasReview($order);
            $has_return = ReturnProduct::hasReturnProduct($order->orderProducts);
            $has_logistics = Order::isHasLogistics($order);

                return [
                    'order_id'       => $order->order_id,
                    'trade_no'       => $order->trade_no,
                    'has_logistics'  => $has_logistics,
                    'has_review'     => $has_review,
                    'has_return'     => $has_return,
                    'order_product'  => $this->getProductImage($order->orderProducts),
                    'product_count'  => $product_count,
//                    'order_totals'   => $order->orderTotalByTotal,
                    'order_totals'   => OrderTotal::getOrderTotalByTotal($order),
                    'order_status'   => $this->assembleOrderStatus($order->orderStatusByLanguage, $has_review),
                ];
        });
    }

    /**
     * @param OrderStatus $order_status
     * @param $has_review
     * @return OrderStatus|array
     */
    private function assembleOrderStatus(OrderStatus $order_status, $has_review)
    {
        // 已完成状态
        if ($has_review && OrderFlowStatus::checkIsToBeEvaluate($order_status->order_status_id)) {

            $order_status = $order_status->toArray();

            $order_status['customer_order_status_name'] = OrderFlowStatus::getOrderFlowStatusName(OrderFlowStatus::ORDER_FLOW_STATUS_ID_COMPLETE);
        }

        return $order_status;
    }


    /**
     * @param Collection $order_products
     * @return Collection
     */
    private function getProductImage(Collection $order_products)
    {
        $order_products->transform(function(OrderProduct $order_product){
            return [
                'name'  => $order_product->name,
                'image' => $order_product->product->image ?? null
            ];
        });

        return $order_products;
    }

    /**
     * @param Order|null $order
     * @param Currency $currency
     * @return array
     */
    private function assembleOrderDetail(Order $order, Currency $currency)
    {
        $order_detail = [];

        $order_detail['shipping'] = [
            'fullname'       => $order->shipping_fullname ?? null,
            'telephone'      => $order->shipping_telephone ?? null,
            'company'        => $order->shipping_company ?? null,
            'address_1'      => $order->shipping_address_1 ?? null,
            'address_2'      => $order->shipping_address_2 ?? null,
            'city'           => $order->shipping_city ?? null,
            'city_id'        => $order->shipping_city_id ?? null,
            'postcode'       => $order->shipping_postcode ?? null,
            'country'        => $order->shipping_country ?? null,
            'country_id'     => $order->shipping_country_id ?? null,
            'zone'           => $order->shipping_zone ?? null,
            'zone_id'        => $order->shipping_zone_id ?? null,
            'county'         => $order->shipping_county ?? null,
            'county_id'      => $order->shipping_county_id ?? null,
        ];

        $order_detail['order_products'] = $this->assembleOrderProducts($order->orderProducts, $currency, $order->currency_value);
        $order_detail['order_totals'] = $this->assembleOrderTotals($order->orderTotals, $currency, $order->currency_value);
        $order_detail['order_info'] = $this->assembleOrderInfo($order);

        return $order_detail;
    }

    /**
     * @param Collection $order_products
     * @param Currency $currency
     * @param $currency_value
     * @return Collection
     */
    public function assembleOrderProducts(Collection $order_products, Currency $currency, $currency_value)
    {
        $order_products->transform(function(OrderProduct $order_product) use($currency, $currency_value){;
            $order_product->image = $order_product->product->image ?? null;
            $order_product->symbol_left = $currency->symbol_left;
            $order_product->symbol_right = $currency->symbol_right;
            $order_product->price = $currency->exchangeOriginPrice($order_product->price, $currency_value);
            $order_product->total = $currency->exchangeOriginPrice($order_product->total, $currency_value);
            $order_product->has_review = OrderProduct::isHasReview($order_product);
            $order_product->has_return = ReturnProduct::hasReturnProduct(collect([$order_product]));

            $order_product->addHidden('product');

            return $order_product;
        });

        return $order_products;
    }

    /**
     * @param Collection $order_totals
     * @param Currency $currency
     * @param $currency_value
     * @return Collection
     */
    private function assembleOrderTotals(Collection $order_totals, Currency $currency, $currency_value)
    {
        $order_totals->transform(function(OrderTotal $order_total) use($currency, $currency_value){
            $order_total->symbol_left = $currency->symbol_left;
            $order_total->symbol_right = $currency->symbol_right;
            $order_total->value = $currency->exchangeOriginPrice($order_total->value, $currency_value);

            return $order_total;
        })
            ->sortBy('sort_order')
            ->values();

        return $order_totals;
    }

    /**
     * @param Order $order
     * @return array
     */
    private function assembleOrderInfo(Order $order)
    {
        $has_review = Order::isHasReview($order);
        // 送达7天后，自动签收日期
        $should_sign_at = isset($order->orderAutoSign->should_sign_at)
            ? (string) $order->orderAutoSign->should_sign_at
            : null;
        // 送达的日期
        $date_delivery = isset($order->orderAutoSign->created_at)
            ? (string) $order->orderAutoSign->created_at
            : null;
        // 付款的日期
        $date_payment = isset($order->orderHistoryByPayment->date_added)
            ? (string) $order->orderHistoryByPayment->date_added
            : null;
        // 订单的完成日期
        $date_complete = isset($order->orderHistoryByComplete->date_added)
            ? (string) $order->orderHistoryByComplete->date_added
            : null;

        return [
            'trade_no'        => $order->trade_no,
            'comment'         => $order->comment,
            'shipping_method' => $order->shipping_method,
            'shipping_code'   => $order->shipping_code,
            'payment_method'  => $order->payment_method,
            'payment_code'    => $order->payment_code,
            'order_status'    => $this->assembleOrderStatus($order->orderStatusByLanguage, $has_review),
            'has_logistics'   => Order::isHasLogistics($order),
            'date_added'      => $order->date_added,
            'date_payment'    => $date_payment,
            'date_delivery'   => $date_delivery,
            'date_complete'   => $date_complete,
            'expire_date'     => $order->expire_date,
            'should_sign_at'  => $should_sign_at,
        ];
    }

    /**
     * @param Customer $customer
     * @return Collection
     */
    public function getCustomerAddress(Customer $customer)
    {
        /** @var Collection $customer_addresses */
        $customer_addresses = (new Address)->where('customer_id', $customer->customer_id)
            ->with('country', 'zone', 'city', 'county')
            ->get();

        $customer_addresses = $customer_addresses->map(function(Address $customer_address) use($customer){
            $customer_address->is_default = ($customer->address_id === $customer_address->getKey());
            $customer_address->country_name = $customer_address->country->name ?? null;
            $customer_address->zone_name = $customer_address->zone->name ?? null;
            $customer_address->city_name = $customer_address->getRelation('city')->name ?? null;
            $customer_address->county_name = $customer_address->county->name ?? null;

            $customer_address->addHidden(['country', 'zone', 'city', 'county']);

            return $customer_address;
        })
            ->sortByDesc(function (Address $address)  {
                return (int) $address->is_default;
            })
            ->values();

        return $customer_addresses;
    }

    /**
     * @param array $shipping_data
     * @return Extension[]|Collection|\Illuminate\Support\Collection
     */
    public function getPaymentMethod(array $shipping_data)
    {
        $geo_zone_ids = ZoneToGeoZone::getGeoZoneIds($shipping_data);

        $payment_extensions = (new Extension)
            ->where('type', Extension::EXTENSION_TYPE_PAYMENT)
            ->get();

        $payment_setting_codes = $payment_extensions->map(function (Extension $extension) {
            return $extension->type . '_' . $extension->code;
        });

        $payment_setting_status_keys = $payment_setting_codes->map(function (string $extension) {
            return $extension . Extension::SETTING_CODE_SUFFIX;
        });

        $payment_setting_geo_zone_id_keys = $payment_setting_codes->map(function (string $extension) {
            return $extension . Extension::SETTING_KEY_GEO_ZONE_ID;
        });

        $payment_setting_sort_order_keys = $payment_setting_codes->map(function (string $extension) {
            return $extension . Extension::SETTING_KEY_SORT_ORDER;
        });

        $payment_setting_total_keys = $payment_setting_codes->map(function (string $extension) {
            return $extension . Extension::SETTING_PAYMENT_KEY_TOTAL;
        });

        // 合法的支付类型
        $setting_status = (new Setting)
            ->whereIn('code', $payment_setting_codes)
            ->whereIn('key', $payment_setting_status_keys)
            ->where('value', Setting::VALUE_STATUS_ON)
            ->get();

        // 合法的支付类型过滤合法geo_zone_id
        $setting_status = (new Setting)
            ->whereIn('code', $setting_status->pluck('code'))
            ->whereIn('key', $payment_setting_geo_zone_id_keys)
            ->whereIn('value', $geo_zone_ids)
            ->get();

        // 排序的值
        $setting_sort_orders = (new Setting)
            ->whereIn('code', $setting_status->pluck('code'))
            ->whereIn('key', $payment_setting_sort_order_keys)
            ->get();

        // 支付最小金额
        $setting_payment_total = (new Setting)
            ->whereIn('code', $setting_status->pluck('code'))
            ->whereIn('key', $payment_setting_total_keys)
            ->get();

        // 合法的geo_zone_id
        $setting_payment_geo_zone_id = (new Setting)
            ->whereIn('code', $setting_status->pluck('code'))
            ->whereIn('key', $payment_setting_geo_zone_id_keys)
            ->get();

        // 合法类型和排序,合法支付最小金额
        $payment_setting_status_keys = $setting_status->map(function (Setting $status) use ($setting_sort_orders, $setting_payment_total, $setting_payment_geo_zone_id) {
            $explode_code = explode(Extension::SETTING_CODE_PREFIX, $status->code);
            $status->type = end($explode_code);
            $status->sort_order = $setting_sort_orders->where('code', $status->code)->first()->value;
            $status->geo_zone_id = $setting_payment_geo_zone_id->where('code', $status->code)->first()->value;
            $status->minimum_total = $setting_payment_total->where('code', $status->code)->first()->value ?? Extension::EXTENSION_PAYMENT_TOTAL_DEFAULT;
            return $status;
        });

        $filter_extensions = $payment_extensions
            ->whereIn('code', $payment_setting_status_keys->pluck('type'))
            ->map(function (Extension $extensions) use ($payment_setting_status_keys) {

                $type_setting = $payment_setting_status_keys->where('type', $extensions->code)->first();

                $extensions->sort_order = $type_setting->sort_order;
                $extensions->icon = ImageHelper::generateImagePath(Extension::EXTENSION_PAYMENT_ICON_MAP[$extensions->code]);
                $extensions->name = Extension::getPaymentExtensionName($extensions->code);
                $extensions->minimum_total = $type_setting->minimum_total;
                $extensions->geo_zone_id = $type_setting->geo_zone_id;

                return $extensions;
            })
            ->sortBy('sort_order')
            ->values();
        return $filter_extensions;
    }


    /**
     * todo "sql_count": 25
     * @return Extension[]|Collection
     */
    public function getTotalMethod()
    {
        $total_extensions = (new Extension)->where('type', Extension::EXTENSION_TYPE_TOTAL)
            ->whereIn('code', EXtension::EXTENSION_TOTAL_CODE_ALLOW)
            ->get();

        $total_extensions = $total_extensions->filter(function(Extension $total_extension){

            $total_method_status = (new Setting)->where('key', Extension::SETTING_CODE_TOTAL_PREFIX.$total_extension->code.Extension::SETTING_CODE_SUFFIX)
                    ->first()
                    ->value ?? null;

            $total_extension->sort_order = (new Setting)->where('key', Extension::SETTING_CODE_TOTAL_PREFIX.$total_extension->code.Extension::SETTING_KEY_SORT_ORDER)
                    ->first()
                    ->value ?? null;

            $total_extension->name = Extension::getTotalExtensionName($total_extension->code);

            return $total_method_status;
        })
            ->sortBy('sort_order')
            ->values();

        return $total_extensions;
    }

    /**
     * @param array $shipping_data
     * @return Extension[]|Collection
     */
    public function getShipping(array $shipping_data)
    {
        $geo_zone_ids = ZoneToGeoZone::getGeoZoneIds($shipping_data);

        $shipping_extension = $this->getShippingMethod($geo_zone_ids);

        return $shipping_extension;
    }

    /**
     * @param \Illuminate\Support\Collection $geo_zone_ids
     * @return Extension[]|Collection
     */
    private function getShippingMethod(\Illuminate\Support\Collection $geo_zone_ids)
    {
        // todo 目前还不完善,可以返回固定运费和称重计费。

        $shipping_extensions = (new Extension)->where('type', Extension::EXTENSION_TYPE_SHIPPING)
            ->get();

        $shipping_extensions = $shipping_extensions->filter(function(Extension $shipping_extension) use($geo_zone_ids) {

            $shipping_method_status = (new Setting)->where('key', Extension::SETTING_CODE_SHIPPING_PREFIX.$shipping_extension->code.Extension::SETTING_CODE_SUFFIX)
                    ->first()
                    ->value ?? null;

            $shipping_method_geo_zone_id = $this->getShippingMethodGeoZoneId($geo_zone_ids, $shipping_extension, $shipping_method_status);

            return ($shipping_method_status && $shipping_method_geo_zone_id);
        })
            ->sortBy('sort_order')
            ->values();

        return $shipping_extensions;
    }

    /**
     * @param \Illuminate\Support\Collection $geo_zone_ids
     * @param Extension $shipping_extension
     * @param $shipping_method_status
     * @return bool
     * @throws \Exception
     */
    private function getShippingMethodGeoZoneId(\Illuminate\Support\Collection $geo_zone_ids, Extension $shipping_extension, $shipping_method_status)
    {
        if (!$shipping_method_status) return false;

        $code = $shipping_extension->code;

        switch($code)
        {
            case Extension::EXTENSION_SHIPPING_CODE_WEIGHT:

                $shipping_method_weight_status = $geo_zone_ids->contains(function(string $geo_zone_id) use($code){

                    $shipping_method_weight_status = (new Setting)->where('key', Extension::SETTING_CODE_SHIPPING_PREFIX . $code .'_'.$geo_zone_id. Extension::SETTING_CODE_SUFFIX)
                            ->first()
                            ->value ?? null;
                    self::$geo_zone_id = $geo_zone_id;

                    return $shipping_method_weight_status;
                });

                if(!$shipping_method_weight_status) return false;

                $shipping_extension->sort_order = (new Setting)->where('key', Extension::SETTING_CODE_SHIPPING_PREFIX.$shipping_extension->code.Extension::SETTING_KEY_SORT_ORDER)
                        ->first()
                        ->value ?? null;
                $shipping_extension->quote = GeoZone::getWeightShipping(self::$geo_zone_id, $code);

                $shipping_extension->geo_zone_id = self::$geo_zone_id;
                $shipping_extension->name = Extension::getShippingExtensionName($shipping_extension->code);

                break;

            default:

                $shipping_method_geo_zone_id = (new Setting)->where('key', Extension::SETTING_CODE_SHIPPING_PREFIX.$code.Extension::SETTING_KEY_GEO_ZONE_ID)
                    ->whereIn('value', $geo_zone_ids)
                    ->first();

                if(!$shipping_method_geo_zone_id) return false;


                $shipping_method_geo_zone_id = $shipping_method_geo_zone_id->value;

                $shipping_extension->sort_order = (new Setting)->where('key', Extension::SETTING_CODE_SHIPPING_PREFIX.$shipping_extension->code.Extension::SETTING_KEY_SORT_ORDER)
                        ->first()
                        ->value ?? null;
                $shipping_extension->quote = GeoZone::getGeoZoneShipping($shipping_method_geo_zone_id, $shipping_extension->code);

                $shipping_extension->geo_zone_id = $shipping_method_geo_zone_id;
                $shipping_extension->name = Extension::getShippingExtensionName($shipping_extension->code);
        }

        return true;
    }


    /**
     * @param $order_data
     * @return mixed
     * @throws \Exception
     */
    public function generateProductOrder($order_data)
    {
        /** @var Currency $currency */
        $currency = Currency::fromEnv();

        $stored = Transaction::handle(function() use($order_data, $currency){
            // 生成order
            /** @var Order $order */
            $order = $this->storeOrder($order_data, $currency);

            // 生成order_total
            $order_total = $this->storeOrderTotal($order, $order_data['total_methods'], $currency);

            // 生成order_product
            $order_product = $this->storeOrderProduct($order, $order_data['products']);

            // 生成order_option
            $order_option = $this->storeOrderOption($order, $order_data['products']);

            // 删除购物车的商品
            if ( ! request()->debug )
            {
                (new ShoppingCartService)->deleteShoppingCartData($order_data['cart_ids'], $order_data['customer_id']);
            }

            return $order;
        });

        return $stored;
    }

    /**
     * @param Order $order
     * @param string $payment_method
     * @return bool
     * @throws \Exception
     */
    public function patchOrderPayment(Order $order, $payment_method)
    {
        $this->checkOrderPaymentCode($order, $payment_method);

        $data = [
            'payment_method'          => Extension::getPaymentExtensionName($payment_method) ?? '',
            'payment_code'            => $payment_method,
        ];

        $updated = $order->update($data);

        return $updated;
    }

    /**
     * @param Order $order
     * @param $order_status_id
     * @param string $comment
     *
     * @return bool
     * @throws \Exception
     */
    public function updateOrderStatus(Order $order, int $order_status_id, string $comment = '')
    {
        $updated = Transaction::handle(function() use($order, $order_status_id, $comment){
            $updated = $order->update(['order_status_id' => $order_status_id]);
            (new OrderHistory)->create([
                'order_id'        => $order->order_id,
                'order_status_id' => $order_status_id,
                'date_added'      => now(),
                'comment'         => $comment,
            ]);

            return $updated;
        });

        return $updated;
    }

    /**
     * @param $order_id
     * @return array
     * @throws \Exception
     */
    public function getOrderLogistics($order_id)
    {
        /** @var Customer $customer */
        $customer = Guard::customer();

        $order = Order::getOrder($order_id);
            $this->checkItemExist($order);
//            $this->checkIsTrue(($order->customer_id === $customer->customer_id), '非法的订单');

        $shipping_shipper_code = $order->shipping_shipper_code;
        $shipping_logistic_code = $order->shipping_logistic_code;
            $this->checkIsTrue(($shipping_shipper_code && $shipping_logistic_code), trans('order.logistic_not_found'));

        $order_logistics = (new Kdniao)->queryTraces($shipping_shipper_code, $shipping_logistic_code);

        // reverse timeline
        $order_logistics['Traces'] = array_reverse($order_logistics['Traces']);

        $order_logistics['ShipperName'] = ExpressCodeMap::get()[$order_logistics['ShipperCode']] ?? null;

        return $order_logistics;
    }

    /**
     * @param $order_data
     * @param $currency
     * @return Order|\Illuminate\Database\Eloquent\Model
     * @throws \App\Exceptions\ApiAbstractException
     */
    private function storeOrder($order_data, Currency $currency)
    {
        /** @var Customer $customer */
        $customer = Guard::customer();

        // 付款人的邮寄地址
        $shipping_address = Address::getAddress($order_data['address_id']);

        $this->checkIsTrue($shipping_address, trans('order.shipping_address_required'));

        // 付款人的默认地址
        $payment_address = $customer->defaultAddress ?? $shipping_address;

        // 订单总金额
        $totals = collect($order_data['total_methods'])->pluck('total', 'code');
//        $order_total = $currency->exchangePrice($totals[Extension::EXTENSION_TYPE_TOTAL]); // 订单价格货币类型和商品默认货币类型相同。
        $order_total = $totals[Extension::EXTENSION_TYPE_TOTAL];

        $ip = request()->getClientIp();
        $date = (string)Carbon::now();
        $order_status_id = Order::STATUS_ID_DEFAULT;

        $order_info = [
              'trade_no'                => TimeHelper::microTimeSerial(),
              'invoice_no'              => 0,             // 发票编号
              'invoice_prefix'          => Setting::getStoreInvoicePrefixById($order_data['store_id']), // 发票抬头
              'store_id'                => $order_data['store_id'],
              'store_name'              => Setting::getStoreNameById($order_data['store_id']),
              'store_url'               => Setting::getStoreUrlById($order_data['store_id']),
              'customer_id'             => $order_data['customer_id'],
              'customer_group_id'       => $customer->customer_group_id,
              'fullname'                => $customer->fullname,
              'email'                   => $customer->email,
              'telephone'               => $customer->telephone,
              'fax'                     => $customer->fax,
              'custom_field'            => '',  // ??
              'payment_fullname'        => $payment_address->fullname,
              'payment_telephone'       => $payment_address->telephone,
              'payment_company'         => $payment_address->company,
              'payment_address_1'       => $payment_address->address_1,
              'payment_address_2'       => $payment_address->address_2,
              'payment_city'            => $payment_address->city()->first()->name ?? '',
              'payment_city_id'         => $payment_address->city_id,
              'payment_postcode'        => $payment_address->postcode,
              'payment_country'         => $payment_address->country->name ?? '',
              'payment_country_id'      => $payment_address->country_id,
              'payment_zone'            => $payment_address->zone->name ?? '',
              'payment_zone_id'         => $payment_address->zone_id,
              'payment_county'          => $payment_address->county->name ?? '',
              'payment_county_id'       => $payment_address->county_id,
              'payment_address_format'  => '',
              'payment_custom_field'    => $payment_address->custom_field,
              'payment_method'          => Extension::getPaymentExtensionName($order_data['payment_method']) ?? '',
              'payment_code'            => $order_data['payment_method'],
              'shipping_fullname'       => $shipping_address->fullname,
              'shipping_telephone'      => $shipping_address->telephone,
              'shipping_company'        => $shipping_address->company,
              'shipping_address_1'      => $shipping_address->address_1,
              'shipping_address_2'      => $shipping_address->address_2,
              'shipping_city'           => $shipping_address->city()->first()->name ?? '',
              'shipping_city_id'        => $shipping_address->city_id,
              'shipping_postcode'       => $shipping_address->postcode,
              'shipping_country'        => $shipping_address->country->name ?? '',
              'shipping_country_id'     => $shipping_address->country_id,
              'shipping_zone'           => $shipping_address->zone->name ?? '',
              'shipping_zone_id'        => $shipping_address->zone_id,
              'shipping_county'         => $shipping_address->county->name ?? '',
              'shipping_county_id'      => $shipping_address->county_id,
              'shipping_address_format' => '',
              'shipping_custom_field'   => $shipping_address->custom_field,
              'shipping_method'         => Extension::getShippingExtensionName($order_data['shipping_method']) ?? '',
              'shipping_code'           => $order_data['shipping_method'],
              'comment'                 => $order_data['comment'],
              'total'                   => $order_total,
              'order_status_id'         => $order_status_id,
              'affiliate_id'            => Order::AFFILIATE_ID_DEFAULT,
              'commission'              => Order::COMMISSION_DEFAULT,
              'marketing_id'            => Order::MARKETING_ID_DEFAULT,
              'tracking'                => '',
              'language_id'             => AppEnv::languageId(),
              'currency_id'             => $currency->currency_id,
              'currency_code'           => $currency->code,
              'currency_value'          => $currency->value,
              'ip'                      => $ip,
              'forwarded_ip'            => $ip,
              'user_agent'              => $_SERVER['HTTP_USER_AGENT'],
              'accept_language'         => request()->getPreferredLanguage() ?? Order::ACCEPT_LANGUAGE_DEFAULT,
              'date_added'              => $date,
              'date_modified'           => $date,
        ];

        $order = (new Order)->create($order_info);

        return $order;
    }

    /**
     * @param Order $order
     * @param array $total_methods
     * @param Currency $currency
     * @return bool
     */
    private function storeOrderTotal(Order $order, array $total_methods, Currency $currency)
    {
        $order_total_data = [];

        foreach ($total_methods as $index => $total_method) {

//            if ($total_method['code'] === Extension::EXTENSION_TOTAL_CODE_SHIPPING) {
//                $value = $currency->exchangeOptionValuePrice($total_method['total']);
//            } else {
//                $value = $currency->exchangePrice($total_method['total']);
//            }
            $value = $total_method['total'];

            $order_total_data[] = [
                'order_id'       => $order->order_id,
                'code'           => $total_method['code'],
                'title'          => $total_method['name'],
                'value'          => $value,
                'sort_order'     => $total_method['sort_order'],
            ];
        }

        $stored = (new OrderTotal)->insert($order_total_data);

        return $stored;
    }

    /**
     * @param $order
     * @param $products
     * @return bool
     * @throws \Exception
     */
    private function storeOrderProduct($order, $products)
    {
        $order_product_data = [];

        foreach ($products as $index => $product_data)
        {
            $order_product_data[] = [
                'order_id'   => $order->order_id,
                'product_id' => $product_data['product_id'],
                'name'       => $product_data['name'],
                'model'      => $product_data['model'],
                'quantity'   => $product_data['quantity'],
                'price'      => $product_data['price'],
                'total'      => $product_data['quantity']*$product_data['price'],
                'tax'        => $product_data['tax'], // todo 税费
                'reward'     => $product_data['points'], // todo 积分
            ];
        }

        $stored = (new OrderProduct)->insert($order_product_data);

        return $stored;
    }

    /**
     * @param Order $order
     * @param $products
     * @return bool
     */
    private function storeOrderOption(Order $order, $products)
    {
        $order_option_data = [];
        $order_products = $order->orderProducts()->oldest('order_product_id')->pluck('order_product_id');

        foreach($products as $index => $product_data)
        {
            $order_product_id = $order_products[$index];

            foreach($product_data['option_values'] as $option_value)
            {
                $option_value['order_id'] = $order->order_id;
                $option_value['order_product_id'] = $order_product_id;

                $order_option_data[] = $option_value;
            }
        }

        $stored = (new OrderOption)->insert($order_option_data);

        return $stored;
    }

    /**
     * @param Customer $customer
     * @return bool
     * @throws \Exception
     */
    private function deleteOrderIsExpired(Customer $customer)
    {
        /** @var Carbon $now */
        $now = Carbon::now();

        // 过期未付款订单改成取消状态。
        $orders = (new Order)->where('customer_id', $customer->customer_id)
            ->where('order_status_id', OrderStatus::ORDER_STATUS_ID_DEFAULT)
            ->where('date_added', '<', $now->subDay())
            ->get();

        $this->deleteOrders($orders);

        return true;
    }

    /**
     * @param Collection $orders
     * @throws \Exception
     * @return bool
     */
    public function deleteOrders(Collection $orders)
    {
        $orders->each(function(Order $order){
            $this->deleteOrder($order);
        });

        return true;
    }

    /**
     * @param Order $order
     *
     * @return bool
     * @throws \Exception
     */
    public function deleteOrder(Order $order): bool
    {
        return Transaction::handle(function() use($order){
            $order->orderProducts()->delete();
            $order->orderTotals()->delete();
            $order->orderOptions()->delete();
            $order->delete();

            return true;
        });
    }

    /**
     * @deprecated (废弃了)
     * @see LogisticTraceController
     * 自动确认收货(order_status_id = 5)
     *
     * @param Customer $customer
     */
    private function completeOrderStatus(Customer $customer)
    {
        // 自动确认收货(快递签收7天后)
        $orders = (new Order)->where('customer_id', $customer->customer_id)
            ->where('order_status_id', OrderStatus::ORDER_STATUS_ID_SHIPPED)
            ->filterLogistics()
            ->get();

        $new_orders = $orders->filter(function(Order $order){
            $order_logistics = (new Kdniao)->queryTraces($order->shipping_shipper_code, $order->shipping_logistic_code);

            $flag = false;

            if ((int)$order_logistics['State'] === ExpressCodeMap::EXPRESS_IS_COMPLETE) {
                $accept_time = array_pop($order_logistics['Traces'])['AcceptTime'];
                $accept_date = (new Carbon($accept_time))->addDay(ExpressCodeMap::EXPRESS_TIME_INTERVAL);

                $flag = Carbon::now()->gte($accept_date);
            }

            return $flag;
        });

        $new_orders->each(function(Order $order){

            $updated = Transaction::handle(function() use($order){
                $updated = $order->update(['order_status_id' => OrderStatus::ORDER_STATUS_ID_COMPLETE]);
                (new OrderHistory)->create([
                    'order_id'        => $order->order_id,
                    'order_status_id' => OrderStatus::ORDER_STATUS_ID_COMPLETE,
                    'date_added'      => (string)Carbon::now(),
                ]);
                return true;
            });
        });
    }

    /**
     * @param Order $order
     * @param $payment_method
     * @throws \App\Exceptions\ApiAbstractException
     */
    private function checkOrderPaymentCode(Order $order, $payment_method)
    {
        $order_total = $order->orderTotalByTotal->value ?? null;
        $shipping_data = [
            'country_id' => $order->shipping_country_id,
            'zone_id'    => $order->shipping_zone_id,
        ];

        $extension_payment_methods = (new OrderService)->getPaymentMethod($shipping_data);

        $payment_method_map = $extension_payment_methods->pluck('minimum_total', 'code')->all();
        $codes = $extension_payment_methods->pluck('code')->all();

        $this->checkIsTrue(in_array($payment_method, $codes), trans('order.payment_method_not_found'));

        $minimum_total = $payment_method_map[$payment_method];
        $this->checkIsTrue($order_total >= $minimum_total, trans('order.pay_minimum_error').$minimum_total);
    }

    /**
     * @return array
     */
    public function getOrderStatistics()
    {
        $customer_id = Guard::customer()->customer_id;

        // 待付款为未付款的的订单数
        $pending_payment_order_num = (new Order)->where('customer_id', $customer_id)
            ->where('order_status_id', OrderStatus::ORDER_STATUS_ID_DEFAULT)
            ->where('date_added', '>=', now()->subDay())
            ->count();

        // 待评价为未评价的订单产品数
        $order_status_ids = OrderFlowStatus::getOrderStatusIds(OrderFlowStatus::ORDER_FLOW_STATUS_ID_TO_BE_EVALUATE);
        $to_be_evaluate_orders = (new Order)->where('customer_id', $customer_id)
            ->filterOrderStatusIds($order_status_ids)
            ->filterIsToBeEvaluate(OrderFlowStatus::ORDER_FLOW_STATUS_ID_TO_BE_EVALUATE)
            ->with('orderProducts.review')
            ->get();
        $to_be_evaluate_num = $to_be_evaluate_orders->sum(function(Order $order){
            $order_product_count = $order->orderProducts->count();

            if ($order_product_count > 1) {
                $order_product_count = $order->orderProducts->sum(function(OrderProduct $order_product){
                    return isset($order_product->review) ? 0 : 1;
                });
            }
            return $order_product_count;

        });

        // 售后为所有申请的售后数量
        $return_product_num = (new ReturnProduct)->where('customer_id', $customer_id)->count();

        return compact('pending_payment_order_num', 'to_be_evaluate_num', 'return_product_num');
    }
}